/*
 * Copyright 2002-2006 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.tigris.jasmincc;

import java.io.FileNotFoundException;
import java.util.*;

import org.tigris.apexam.*;
import org.tigris.jasmincc.configuration.*;
import org.tigris.jasmincc.framework.*;
import org.tigris.jasmincc.framework.namespace.*;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * A compiler compiler front-end framework for bringing object-oriented
 * characteristcs for various compiler compilers.
 * 
 * @author Sam Huang
 * 
 */
public class JasminCCMain {

	private static final int major = 0;

	protected static final int minor = 1;

	protected static final double patch = 2;

	protected ConfigurationManager configurationManager = null;

	protected static Log logger = LogFactory.getLog(JasminCCMain.class);

	public static void main(String[] args) throws Exception {
		MessageFactory messageFactory = (MessageFactory) SimpleObjectFactory
				.getInstance().getBeanFactory().getBean("messageFactory");
		Map<String, Object> params = new HashMap<String, Object>();

		JasminCCMain jasmincc = new JasminCCMain();
		params.put("major", jasmincc.getMajor());
		params.put("minor", jasmincc.getMinor());
		params.put("patch", jasmincc.getPatch());

		String version = messageFactory.getMessage("JasminCCMain.version",
				params);

		params.clear();
		params.put("version", version);
		logger.info(messageFactory.getMessage(JasminCCMain.class
				.getSimpleName()
				+ ".info", params));
		if (args.length < 1) {
			logger.trace(messageFactory.getMessage(JasminCCMain.class
					.getSimpleName()
					+ ".usage", params));
			return;
		}

		String lastArgument = args[args.length - 1];

		JavaCCStub javacc = (JavaCCStub) SimpleObjectFactory.getInstance()
				.getBeanFactory().getBean("javacc");
		JJTreeStub jjtree = (JJTreeStub) SimpleObjectFactory.getInstance()
				.getBeanFactory().getBean("jjtree");

		if (lastArgument.endsWith(".xml")) {
			ConfigurationManager configurationManager = (ConfigurationManager) SimpleObjectFactory
					.getInstance().getBeanFactory().getBean(
							"configurationManager");
			configurationManager.init(lastArgument);
			jasmincc.setConfigurationManager(configurationManager);
			jasmincc.build();
		} else if (lastArgument.endsWith(".jj")) {
			javacc.compile(args);
		} else if (lastArgument.endsWith(".jjt")) {
			jjtree.compile(args);
		} else if (lastArgument.endsWith(".jcc")) {
			throw new Exception(
					"Please use qualified grammar name instead of a file name.");
		} else {
			jasmincc.setArguments(args);
			jasmincc.compile();
		}
	}

	String[] args = new String[1];

	protected Coordinator coordinator = null;

	protected JavaCCStub javacc = null;

	protected JJTreeStub jjtree = null;

	protected String grammarName;

	public JasminCCMain() throws Exception {
		// configurationHandler = (ConfigurationManager) SimpleObjectFactory
		// .getInstance().getBeanFactory().getBean("configurationManager");
		coordinator = (Coordinator) SimpleObjectFactory.getInstance()
				.getBeanFactory().getBean("coordinator");
		javacc = (JavaCCStub) SimpleObjectFactory.getInstance()
				.getBeanFactory().getBean("javacc");
		jjtree = (JJTreeStub) SimpleObjectFactory.getInstance()
				.getBeanFactory().getBean("jjtree");
	}

	/**
	 * Build grammars defined in configuration.
	 * 
	 * @throws Exception
	 */
	public void build() throws Exception {
		List<String> parameterList = new ArrayList<String>();
		for (Iterator<Object> iterator = configurationManager.getOptions()
				.keySet().iterator(); iterator.hasNext();) {
			String optionName = (String) iterator.next();
			parameterList.add("-" + optionName + "="
					+ configurationManager.getOptions().get(optionName));
		}

		String[] params = new String[parameterList.toArray(new String[] {}).length + 1];
		System.arraycopy(parameterList.toArray(new String[] {}), 0, params, 0,
				parameterList.toArray(new String[] {}).length);
		for (String grammarName : configurationManager.getBuilds()) {
			params[params.length - 1] = grammarName;
			setArguments(params);
			for (String arg : params) {
				logger.info("option: " + arg);
			}
			compile();
		}
	}

	public void compile() throws Exception {
		compile(this.grammarName);
	}

	public void compile(String grammarName) throws Exception {
		int lastArgument = args.length - 1;
		JavaCCModel javaccModel = coordinator.crosscompile(grammarName);
		JavaCCFile file = javaccModel.render();
		args[lastArgument] = file.getAbsolutePath();
		if (file.isJavaCCFile()) {
			javacc.compile(args);
		} else if (file.isJJTreeFile()) {
			jjtree.compile(args);
			String outputLocation = getOutputLocation(args);
			if (outputLocation == null) {
				throw new FileNotFoundException(
						"Please specify an output directory before compile.");
			}
			String fileName = file.getName();
			int dot = fileName.lastIndexOf('.');
			fileName = fileName.substring(0, dot);
			if (outputLocation.endsWith("\\")) {
				fileName = outputLocation + fileName + ".jj";
			} else {
				fileName = outputLocation + "\\" + fileName + ".jj";
			}
			logger.info(fileName);
			JavaCCFile javaccFile = new JavaCCFile(fileName);
			if (javaccFile == null) {

			}
			args[lastArgument] = fileName;
			javacc.compile(args);
		}
	}

	protected static String getOutputLocation(String[] args) {
		for (String arg : args) {
			if (arg.startsWith("-OUTPUT_DIRECTORY")) {
				return arg.substring("-OUTPUT_DIRECTORY".length() + 1, arg
						.length());
			}
		}
		return null;
	}

	public NamespaceManager getNamespaceManager() throws Exception {
		return (NamespaceManager) SimpleObjectFactory.getInstance()
				.getBeanFactory().getBean("namespaceManager");
	}

	public void setArguments(String[] args) {
		this.args = args;
		int lastArgument = args.length - 1;
		this.grammarName = args[lastArgument];
	}

	/**
	 * @param configurationHandler
	 *            the configurationHandler to set
	 */
	public void setConfigurationManager(
			ConfigurationManager configurationHandler) {
		this.configurationManager = configurationHandler;
	}
	
	/**
	 * @return the major
	 */
	public int getMajor() {
		return major;
	}

	/**
	 * @return the minor
	 */
	public int getMinor() {
		return minor;
	}

	/**
	 * @return the patch
	 */
	public double getPatch() {
		return patch;
	}

}
